﻿using System;
using System.Collections.Generic;

namespace Core.Res
{

    public class RelyQueue
    {
        /// <summary>
        /// 0号队列专门处理Batch类型
        /// </summary>
        private readonly List<Queue<LoadAwaiter>> mWorking;

        private readonly Queue<LoadAwaiter> mWaiting;
        private readonly HashSet<string> mLoadingSet;
        private readonly Dictionary<string, int> idxDict;

        public RelyQueue(int concurrent_cnt)
        {
            if (concurrent_cnt == 0)
            {
                throw new Exception("Error! LoadQueue.concurrent_cnt need bigger then 0");
            }
            mWorking = new List<Queue<LoadAwaiter>>();
            for (int i = 0; i <= concurrent_cnt; i++)
            {
                mWorking.Add(new Queue<LoadAwaiter>());
            }
            mWaiting = new Queue<LoadAwaiter>();
            mLoadingSet = new HashSet<string>();

            idxDict = new();

        }

        public bool Contains(string asset_name)
        {
            return mLoadingSet.Contains(asset_name);
        }

        public int LoadCnt => mLoadingSet.Count;

        private int GetShortest()
        {
            int min_cnt = int.MaxValue;
            int shortest_idx = 1;

            for (int i = 1; i < mWorking.Count; i++)
            {
                var queue = mWorking[i];
                if (queue.Count < min_cnt)
                {
                    min_cnt = queue.Count;
                    shortest_idx = i;
                }
            }
            return shortest_idx;
        }

        private int GetSameIdx(LoadAwaiter item)
        {
            int idx = -1;
            for (int i = 1; i < mWorking.Count; i++)
            {
                var queue = mWorking[i];
                foreach (var o in queue)
                {
                    if (string.CompareOrdinal(o.Name, item.Name) == 0)
                    {
                        idx = i;
                        break;
                    }
                }
                if (idx >= 0)
                {
                    break;
                }
            }
            return idx;
        }

        public void Update()
        {
            WorkingUpdate();
            WaitingUpdate();
        }

        private void WaitingUpdate()
        {
            if (mWaiting.Count <= 0)
            {
                return;
            }

            idxDict.Clear();

            var cnt = mWaiting.Count;
            for (int i = 0; i < cnt; i++)
            {
                var v = mWaiting.Peek();
                v.RefreshRely();
                var name = v.Name;
                if (v.Rely == null || v.Rely.Count == 0)
                {
                    mWaiting.Dequeue();

                    int idx;

                    if (idxDict.ContainsKey(name))
                    {
                        idx = idxDict[name];
                    }
                    else
                    {
                        idx = GetSameIdx(v);
                        idxDict[name] = idx;
                    }

                    if (idx < 0)
                    {
                        idx = GetShortest();
                        idxDict[name] = idx;
                    }

                    //log.err($"working idx={result_idx}");
                    mWorking[idx].Enqueue(v);
                }
            }
        }

        private void WorkingUpdate()
        {
            for (int i = 0; i < mWorking.Count; i++)
            {
                var queue = mWorking[i];

                while (queue.Count > 0)
                {
                    var vo = queue.Peek();
                    if (vo.State == ELoadState.Invalid)
                    {
                        vo.Start();
                    }
                    else if (vo.State == ELoadState.Loading)
                    {
                        vo.Update();
                    }

                    if (vo.State != ELoadState.Complete)
                    {
                        break;
                    }

                    vo.Finish();
                    mLoadingSet.Remove(vo.Name);
                    if (queue.Count > 0)
                    {
                        queue.Dequeue();
                    }
                }
            }
        }

        public void Enqueue(LoadAwaiter item)
        {
            if (string.IsNullOrEmpty(item.Name))
            {
                //Batch 统一放第0个队列
                mWorking[0].Enqueue(item);
            }
            else
            {
                mWaiting.Enqueue(item);
                //log.err($"waiting name={item.Name} cnt={m_waiting.Count}");
                mLoadingSet.Add(item.Name);
            }
        }
    }
}
